<?php
declare(strict_types=1);

namespace app\api\service;
use app\api\model\business\EntrustRecords;
use app\api\model\business\PurchaseRecords;
use app\api\model\Order;
use app\api\model\OrderGoods;
use app\common\enum\coupon\CouponType;
use app\common\enum\order\OrderStatus;
use app\common\enum\order\PayStatus;
use app\common\enum\order\PayType;
use app\common\enum\OrderType;
use app\common\model\activity\LuckyBag as BagModel;
use app\common\model\lottery\LotteryParticipant;
use app\common\model\lottery\Manage;
use app\common\model\PaymentLog;
use app\common\model\Seckill as SeckillModel;
use app\common\model\User as UserModel;
use app\common\model\UserCoupon;
use app\common\service\BaseService;
use app\common\service\Buy;
use think\Exception;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Env;
use think\facade\Event;
use app\common\model\order\Goods as OrderGoodsModel;
use app\common\model\GoodsSku;
use think\facade\Log;

/**
 * 活动订单
 */
class ActivityOrder extends BaseService
{
    private static  $list_row = 15;

    //优惠抵扣
    public function discountCalc($discount_type,$origin_price,$coupon_id = 0,$coupon_num = 1): array
    {
        $user_id = User::getCurrentLoginUserId();
        //优惠券
        if($discount_type == 1){
            $coupon = UserCoupon::where(['user_coupon_id' => $coupon_id,'is_use' => 0,'user_id' => $user_id])->find();
            if(!$coupon){
                throwError('优惠券不存在或已被使用');
            }
            //todo  判断数量-预存卡可以多张使用
            if($coupon->coupon_type == CouponType::PRE_STORAGE_CARD){
                $own_num = UserCoupon::where(['user_id' => $user_id,'coupon_type' => CouponType::PRE_STORAGE_CARD,'is_use' => 0])->count();
                if($own_num < $coupon_num){
                    throwError('可用优惠券数量不足');
                }
                $reduce_price = $coupon->reduce_price * $coupon_num;
                $discounted_price = $origin_price - $reduce_price;
                if ($discounted_price < 0 ) $discounted_price = 0;

            }elseif($coupon->coupon_type == CouponType::DISCOUNT){
                //折扣劵-打折
                $discounted_price = $origin_price;
                $reduce_price = 0;
                if($coupon->discount != 0){
                    $discounted_price = $origin_price * $coupon->discount / 10;
                    $reduce_price = $origin_price * (10 - $coupon->discount) / 10;
                }

            }else{
                $reduce_price = $coupon->reduce_price;
                $discounted_price = $origin_price - $coupon->reduce_price;
                if ($discounted_price < 0 ) $discounted_price = 0;
            }

        }else{
            $user_points = UserModel::where('user_id',$user_id)->value('points');
            $allow_deduce_price = $user_points / intval(Env::get('game.exchange_scale'));

            if($allow_deduce_price >= $origin_price){
                $pay_price = 0;
                $points_money = $origin_price;
                $points_num = $origin_price * intval(Env::get('game.exchange_scale'));
            }else{
                $pay_price = $origin_price - $allow_deduce_price;
                $points_money = $allow_deduce_price;
                $points_num = $points_money * intval(Env::get('game.exchange_scale'));
            }

            $reduce_price = $allow_deduce_price;
            $discounted_price = $pay_price;
        }
        return [
            'reduce_price' => sprintf("%0.2f", $reduce_price),    //抵扣金额
            'discounted_price' => sprintf("%0.2f", $discounted_price),    //折后价
            'points_asset' => [
                'points_money' => sprintf("%0.2f",$points_money ?? 0),
                'points_num' => $points_num ?? 0,
            ]
        ];
    }


    //计算
    public function calcOrder($params): array
    {
        $check_buy = (new Buy())->checkMobile();
        if(!$check_buy){
            throwError('手机号未绑定',101);
        }

        if($params['type'] == 1){
            $this->checkLotteryPay($params['id']);
            $pay_money = Manage::where('id', $params['id'])->value('price');
        }else{
            $this->checkSecKillPay($params['id']);
            $pay_money = SeckillModel::where('id', $params['id'])->value('price');
        }
        $userId = User::getCurrentLoginUserId();
        $assets = UserModel::where('user_id', $userId)->field('balance,points')->find()->toArray();
        return [
            'pay_money' => sprintf("%01.2f", $pay_money),
            'discount' => 0, // 折扣
            'deduct' => 0, // 抵扣
            'assets' => $assets,
            'pay_money_point' => $this->getPointPayMoney($assets,$pay_money),
        ];
    }

    //积分折后价
    public static function getPointPayMoney($assets,$origin_pay_money):string
    {
        $pay_money_point = $origin_pay_money;
        if($assets['points'] > 0){
            $allow_deduce_price = $assets['points'] / Env::get('game.exchange_scale');
            if($allow_deduce_price >= $origin_pay_money){
                $pay_money_point = 0;
            }else{
                $pay_money_point = $origin_pay_money - $allow_deduce_price;
            }
        }
        return sprintf('%0.2f',strval($pay_money_point));
    }

    //运费劵抵扣价
    public static function getShippingCouponPayMoney($shipping_coupon,$origin_pay_money):string
    {
         $shipping_deduct_price = $origin_pay_money;
         if(!empty($shipping_coupon)){
             $allow_deduce_price = $shipping_coupon['reduce_price'];
             if($allow_deduce_price > $origin_pay_money){
                 $shipping_deduct_price = 0;
             }else{
                 $shipping_deduct_price = $origin_pay_money - $allow_deduce_price;
             }
         }
        return sprintf('%0.2f',strval($shipping_deduct_price));
    }




    //创建订单
    public function createOrder($params): ?array
    {
        if($params['type'] == 1){
            $this->checkLotteryPay($params['id']);
            $manage = Manage::find($params['id']);
            $res = $this->createLotteryOrder($params,$manage);
        }else{
            $this->checkSecKillPay($params['id']);
            $seckill = SeckillModel::find($params['id']);
            $res = $this->createSeckillOrder($params,$seckill);
        }
        return $res;
    }

    //支付校验处理--通用方法
    public function checkDiscountPay($params,$calc,$user_id):array
    {
        $user = UserModel::where('user_id', $user_id)->field("user_id,balance")->lock(true)->find();
        if($params['discount_type'] != 0){
            $discount_info = $this->discountCalc($params['discount_type'],$calc['pay_money'],$params['coupon_id'] ?? 0,$params['coupon_num'] ?? 1);
            if($params['pay_type'] == PayType::BALANCE){
                if ($user->balance + $discount_info['reduce_price'] < $calc['pay_money']) {
                    throwError("余额不足");
                }
            }
            $pay_price = $discount_info['discounted_price'];
            $points_money = $discount_info['points_asset']['points_money'];
            $points_num = $discount_info['points_asset']['points_num'];
            if($params['discount_type'] == 1){
                $coupon_money = $discount_info['reduce_price'];
            }

        }else{
            //不使用任何优惠处理
            if ($params['pay_type'] == PayType::BALANCE) {
                if ($user->balance < $calc['pay_money']) {
                    throwError("余额不足");
                }
            }
            $pay_price = $calc['pay_money'];
        }

        return [
            'pay_price' => $pay_price,
            'points_money' => $points_money ?? 0,
            'points_num' => $points_num ?? 0,
            'coupon_id' => $params['coupon_id'] ?? 0,
            'coupon_money' => $coupon_money ?? 0,
            'coupon_num' => $params['coupon_num'] ?? 0,
        ];
    }



    //创建抽选订单
    public function createLotteryOrder($params,Manage $manage)
    {
        $redis = Cache::store('redis');
        $lockKey = "lottery_activity:" . $params['id'];
        $expire = 2;
        $lockValue = time() + $expire;
        $lock = $redis->setNx($lockKey, $lockValue);
        // 获得锁
        if (!empty($lock) || ($redis->get($lockKey) < time() && $redis->getSet($lockKey, $lockValue) < time())) {
            $redis->expire($lockKey, $lockValue);
            $user_id = User::getCurrentLoginUserId();
            $calc = $this->calcOrder($params);
            $deal_money = $this->checkDiscountPay($params,$calc,$user_id);
            Db::startTrans();
            try {
                // 创建订单
                $order = Order::create([
                    'user_id' => $user_id,
                    'order_type' => OrderType::LOTTERY,
                    'activity_id' => $params['id'],
                    'goods_id' => $manage->goods_id,
                    'total_price' => $calc['pay_money'],
                    'pay_type' => $params['pay_type'],
                    'order_no' => orderNo(),
                    'pay_status' => PayStatus::PENDING,
                    'platform' => getPlatform() ?? 'system',
                    'pay_price' => $deal_money['pay_price'],
                    'points_money' => $deal_money['points_money'],
                    'points_num' => $deal_money['points_num'],
                    'coupon_id' => $deal_money['coupon_id'],
                    'coupon_num' => $deal_money['coupon_num'],
                    'coupon_money' => $deal_money['coupon_money']
                ]);
                if (!$order->order_id) {
                    throw new Exception("创建订单失败");
                }
                //创建订单商品表
                $goods_sku_id = GoodsSku::where('goods_id',$manage->goods_id)->value('id');
                $order_goods = OrderGoodsModel::create([
                    'goods_id' => $manage->goods_id,
                    'goods_name' => $manage->name,
                    'image_id' => $manage->cover_id,
                    'goods_sku_id' => $goods_sku_id ?? 0,
                    'content' => '抽选商品',
                    'goods_price' => $manage->price,
                    'order_id' => $order->order_id,
                    'user_id' => $user_id,
                    'create_time' => time(),
                    'total_price' => $manage->price,
                    'total_pay_price' => $manage->price,
                ]);
                if(!$order_goods->order_goods_id){
                    throw new Exception("创建订单商品失败");
                }
                // 支付信息
                (new \app\api\model\PaymentLog())->paymentRecord(
                    $this->storeId,
                    $order->order_no,
                    strval($order->pay_price),
                    'LotteryOrderPay',
                    ['business' => 'lottery_order', 'expire_time' => time() + 180]
                );
                Db::commit();
                if ($redis->ttl($lockKey)) {
                    $redis->del($lockKey);
                }
                return ['order_no' => $order->order_no];
            } catch (Exception $exception) {
                Db::rollback();
                $redis->del($lockKey);
                throwError($exception->getMessage());
            }
        } else {
            throwError("系统繁忙");
        }
    }

    //创建秒杀订单
    public function createSeckillOrder($params,SeckillModel $seckill){
        $redis = Cache::store('redis');
        $lockKey = "seckill_activity:" . $params['id'];
        $expire = 2;
        $lockValue = time() + $expire;
        $lock = $redis->setNx($lockKey, $lockValue);
        // 获得锁
        if (!empty($lock) || ($redis->get($lockKey) < time() && $redis->getSet($lockKey, $lockValue) < time())) {
            $redis->expire($lockKey, $lockValue);
            $user_id = User::getCurrentLoginUserId();
            $calc = $this->calcOrder($params);
            $deal_money = $this->checkDiscountPay($params,$calc,$user_id);
            Db::startTrans();
            try {
                // 创建订单
                $order = Order::create([
                    'user_id' => $user_id,
                    'order_type' => OrderType::SECKILL,  //秒杀
                    'activity_id' => $params['id'],
                    'goods_id' => $seckill->goods_id,
                    'total_price' => $calc['pay_money'],
                    'pay_price' => $deal_money['pay_price'],
                    'pay_type' => $params['pay_type'],
                    'order_no' => orderNo(),
                    'pay_status' => PayStatus::PENDING,
                    'platform' => getPlatform() ?? 'system',
                    'points_money' => $deal_money['points_money'],
                    'points_num' => $deal_money['points_num'],
                    'coupon_id' => $deal_money['coupon_id'],
                    'coupon_num' => $deal_money['coupon_num'],
                    'coupon_money' => $deal_money['coupon_money']
                ]);
                if (!$order->order_id) {
                    throw new Exception("创建订单失败");
                }
                //创建订单商品表
                $goods_sku_id = GoodsSku::where('goods_id',$seckill->goods_id)->value('id');
                $image_ids = $seckill->image_id;
                $image_id = $image_ids[0]['file_id'] ?? 0;
                $order_goods = OrderGoodsModel::create([
                    'goods_id' => $seckill->goods_id,
                    'goods_name' => $seckill->name,
                    'image_id' => $image_id,
                    'goods_sku_id' => $goods_sku_id ?? 0,
                    'content' => '抽选商品',
                    'goods_price' => $seckill->price,
                    'order_id' => $order->order_id,
                    'user_id' => $user_id,
                    'create_time' => time(),
                    'total_price' => $seckill->price,
                    'total_pay_price' => $seckill->price,
                ]);
                if(!$order_goods->order_goods_id){
                    throw new Exception("创建订单商品失败");
                }

                // 扣除库存和增加销量
                $seckill->sale  = Db::raw('sale+1');
                $res = $seckill->save();
                if (!$res) {
                    throw new Exception("秒杀订单更新库存失败");
                }

                // 支付信息
                (new \app\api\model\PaymentLog())->paymentRecord(
                    $this->storeId,
                    $order->order_no,
                    strval($order->pay_price),
                    'SeckillOrderPay',
                    ['business' => 'seckill_order', 'expire_time' => time() + 180]
                );
                Db::commit();
                if ($redis->ttl($lockKey)) {
                    $redis->del($lockKey);
                }
                return ['order_no' => $order->order_no];
            } catch (Exception $exception) {
                Db::rollback();
                $redis->del($lockKey);
                Log::info($exception->getMessage());
                throwError($exception->getMessage());
            }
        } else {
            throwError("系统繁忙");
        }
    }

    /**支付
     * @param $params
     * @return array
     */
    public function pay($params): array
    {
        $order = Order::where([
            'order_no' => $params['order_no'],
            'user_id' => User::getCurrentLoginUserId()
        ])->find();
        if (!$order) {
            throwError("订单异常");
        }
        $payment = PaymentLog::where('order_no', $params['order_no'])->find();
        if ($payment->pay_status != 0) {
            throwError("订单已支付或已失效");
        }

        if ($payment->expire_time < time()) {
            throwError("订单已失效");
        }
        // 获取支付信息
        $wechat = $alipay = [];
        if ($params['pay_type'] == PayType::BALANCE) {
            Event::trigger('PaymentHandle', ['payment' => $payment->toArray(), 'payType' => PayType::BALANCE]);
        } elseif($params['pay_type'] == PayType::WECHAT) {
            $wechat = Payment::wechat($order->order_id, $payment->order_no, $payment->pay_price, OrderType::OTHERS);
        }elseif($params['pay_type'] == PayType::ALIPAY){
            //支付宝
            $aliPayment =  app(\app\api\service\alipay\Payment::class);
            $postData = [
                'order_no' => $params['order_no'],
                'total_amount' => $order->pay_price,
                'quit_url' => ''
            ];
            $url = $aliPayment->app($postData);
            $alipay = [
                'order_id' => $params['order_no'],
                'pay_url' => $url
            ];
        }else{
            throwError('不正确的支付方式');
        }
        return [
            'order_no' => $params['order_no'],
            'pay_type' => $params['pay_type'],
            'wechat' => $wechat,
            'alipay' => $alipay
        ];
    }

    //校验抽选
    public function checkLotteryPay(int $lottery_id):bool
    {
        $condition = [
            ['id','=',$lottery_id],
            ['status','=',1],
            ['is_del','=',0]
        ];
        $lottery = Manage::where($condition)->find();
        if(!$lottery){
            throwError('抽选活动已关闭');
        }
        $user_id = User::getCurrentLoginUserId();
        //检查是否中签，防止恶意购买
        $winners = LotteryParticipant::where('lottery_id', $lottery_id)
            ->whereNotNull('win_time')
            ->field('user_id')
            ->group('user_id')
            ->select()
            ->toArray();
        if(empty($winners)) throwError('本次活动没有人中签，无法购买');
        $winner_user_ids = array_column($winners,'user_id');
        if (!in_array($user_id, $winner_user_ids)) {
            throwError('未中签，无法购买');
        }
        $condition = [
            'activity_id' => $lottery_id,
            'user_id' => User::getCurrentLoginUserId(),
            'pay_status' => 20,
            'order_type' => OrderType::LOTTERY
        ];
        $order = Order::where($condition)->find();
        if($order){
            throwError('不能重复购买');
        }
        return true;
    }


    //校验秒杀
    public function checkSecKillPay(int $seckill_id): bool
    {
        $condition = [
            ['id','=',$seckill_id],
            ['status','=',1],
            ['is_del','=',0]
        ];
        $seckill = (new SeckillModel())->where($condition)->lock(true)->find();
        if(!$seckill){
            throwError('秒杀活动已关闭');
        }
        //校验时间
        $now = date('Y-m-d H:i:s');
        if(!empty($seckill->start_time) && !empty($seckill->end_time)){
            if($seckill->start_time > $now){
                throwError('秒杀未开始');
            }
            if($seckill->end_time < $now){
                throwError('秒杀已结束');
            }
        }
        //库存
        $remain = $seckill->quantity - $seckill->sale;
        if($remain <= 0){
            throwError('商品已卖完,下次再来吧');
        }
        return true;
    }

    //订单列表
    public function getList($params): array
    {
        $condition = [];
        if(!empty($params['month'])){
            if($params['month'] == 1){
                $condition[] = ['pay_time', '>', time()-86400 * 30];
            }elseif ($params['month'] == 2){
                $condition[] = ['pay_time', '>', time()-86400 * 30 * 3];
            }elseif ($params['month'] == 3){
                $condition[] = ['pay_time', '>', time()-86400 * 30 * 6];
            }
        }
        return \app\common\model\Order::where('is_delete',0)
            ->with(['goods.image'])
            ->where('user_id',User::getCurrentLoginUserId())
            ->where('order_status',30)
            ->where('pay_status',20)
            ->where('order_type','<>',4)
            ->where($condition)
            ->field('order_id,pay_price,pay_time,goods_id,user_id,order_status,pay_status,order_type,activity_id,order_source_id')
            ->order('create_time','DESC')
            ->paginate($params['limit'] ?? self::$list_row)
            ->each(function (&$item){
                $item['cover_url'] = $item['goods'][0]['goods_image'] ?? '';
                $item['name'] = $item['goods'][0]['goods_name'] ?? '';
                $item['type'] = $item['goods'][0]['type'] ?? 0;   //类型
                if($item['order_type'] == 3){
                    $bag_info = BagModel::with(['image'])
                        ->where('activity_id', $item['activity_id'])
                        ->select()
                        ->toArray();
                    $reward_goods_num = OrderGoodsModel::where('order_id',$item['order_id'])->count();
                    $item['cover_url'] = $bag_info[0]['image_url'] ?? '';  //抽赏用的封面是活动首页图
                    $item['name'] = $bag_info[0]['name'] ?? '';
                    $item['num'] = $reward_goods_num;
                }
                elseif ($item['order_type'] == 5){
                    //寄售订单(购买)
                    $entrust_records = (new EntrustRecords())->where('id','=',$item['order_source_id'])->find();
                    $entrust_records = $entrust_records->toArray();
                    $item['cover_url'] = $entrust_records['goods_info']['goods_image'] ?? '';
                    $item['name'] = $entrust_records['goods_info']['goods_name'] ?? '';
                    $item['num'] = $entrust_records['goods_num'] ?? 0;

                }elseif ($item['order_type'] == 7){
                    //求购订单
                    $purchase_records = (new PurchaseRecords())->where('id','=',$item['order_source_id'])->find();
                    $purchase_records = $purchase_records->toArray();
                    $item['cover_url'] = $purchase_records['goods_info']['goods_image'] ?? '';
                    $item['name'] = $purchase_records['goods_info']['goods_name'] ?? '';
                    $order_goods = (new OrderGoods())->where('order_id','=',$item['order_id'])->find();
                    $item['num'] = $order_goods->total_num ?? 0;
                }
                else{
                    $item['num'] = 1;
                }
                unset($item['goods'],$item['state_text']);
            })
            ->toArray();
    }


    /**获取订单状态
     * @param $order_no
     * @return array
     */
    public function getPayStatus($order_no):array
    {
        $condition = [
            ['order_no','=',$order_no],
        ];
        $is_pay = 0;
        $order = (new Order())->where($condition)->find();
        if(!$order){
            throwError('订单不存在');
        }
        if($order->pay_status == PayStatus::SUCCESS && $order->order_status == OrderStatus::COMPLETED){
            $is_pay = 1;
        }
        return [
            'is_pay' => $is_pay
        ];
    }

}